package rtmp;

import decoder.AudioStreamDecoder;
import util.Common;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import lombok.Getter;
import lombok.Setter;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

//  +--------------+     +-------------+----------------+-------------------+
//          | Chunk Header |  =  | Basic header| Message Header |Extended Timestamp |
//          +--------------+     +-------------+----------------+-------------------+
public class RtmpPacket {
    static Logger logger = Logger.getLogger(RtmpPacket.class);

    @Setter
    @Getter
    protected int csid;

    private Map<Integer, HeadInfo> csidHeadInfo = new HashMap<Integer, HeadInfo>();

    @Setter
    @Getter
    protected int head_len; //头部长度

    @Setter
    @Getter
    protected int timestamp = 0;

    private int prevVideoTimestamp = 0;

    private int prevAudioTimestamp = 0;

    @Setter
    @Getter
    private int renderTimestamp = 0;


    private boolean isExtAudioTimestamp = false;

    private boolean isExtVideoTimestamp = false;


    @Setter
    @Getter
    private int videoTimestamp;

    @Setter
    @Getter
    private int audioTimestamp;

    @Setter
    @Getter
    protected int messageLength; //消息数据长度

    @Setter
    @Getter
    protected RtmpMessage messageType; //消息类型

    @Setter
    @Getter
    protected int streamId; //流id

    @Setter
    @Getter
    protected int extTimestamp; //额外时间戳

    public int type = 0;

    @Setter
    @Getter
    private boolean lackMessage = false;

    @Getter
    private byte[] messageData;

    private int fmt; //basic head fmt

    private boolean isLackHead = false;

    @Setter
    private int chunkLength = Common.DEFAULT_CHUNK_MESSAGE_LENGTH;

    @Setter
    @Getter
    private ByteBuf byteBuf;

    private ByteBuf copyByteBuf;

    //是否数据分包
    @Setter
    @Getter
    private boolean isSubpackage = false;

    private int readIndex = 0;

    @Getter
    private ChannelHandlerContext ctx;

    private int prevLen = 0;

    File txt;

    private AudioStreamDecoder audioStreamDecoder = new AudioStreamDecoder();
    //对应的 chunk head length 长度
    private int[] chunk_message_length = {11, 7, 3, 0};

    // Basic head 类型
//    private enum BasicHead {
//        FULL,   // Basic header
//        NO_MSG_STREAM_ID,
//        TIMESTAMP, // no timestamp
//        ONLY // chunk header == null
//    };


    private void setCsid(byte flag) {
        this.csid = (byte) ((flag & 0xff & 0xff) & 0x3f); // 按位与 11 为 1 ，有0 为 0
        switch (this.csid){
            case 0:
                if (byteBuf.readableBytes() < 1) { //头部数据太少
                    logger.info("csid头部数据不足");
                    lackMessage = true;
                    return;
                }
                logger.info("rtmphead =  0");;
                this.csid = byteBuf.readByte() + 64;
                break;
            case 1:
                logger.info("rtmphead =  0");
                if (byteBuf.readableBytes() < 2) { //头部数据太少
                    logger.info("csid2头部数据不足");
                    lackMessage = true;
                    return;
                }
                this.csid = byteBuf.readByte() * 256 + byteBuf.readByte() + 64;
                break;
        }
    }

    private void init() {

        lackMessage = false;
        isLackHead = false;
        if (byteBuf.readableBytes() < 1) { //头部数据太少
            logger.info("头部数据不足");
            lackMessage = true;
            return;
        }
        byte flag = byteBuf.readByte();
//        logger.info(Common.bytes2hex(new byte[]{flag}));
        setCsid(flag);
        this.fmt = (byte) ((flag & 0xff & 0xff) >> 6);
        this.head_len = chunk_message_length[this.fmt];
        if(this.type == 1){
            logger.info("head len "+this.head_len);
        }
        if(setChunkHeaderData()){
            if(this.type == 1){
                logger.info("messageLen "+this.messageLength);
                logger.info("messageType "+this.messageType);
                logger.info("chunkLength "+this.chunkLength);
            }
            setMessageData();
        }
    }

    public RtmpPacket(ChannelHandlerContext ctx, ByteBuf byteBuf) {
        this.ctx = ctx;
        logger.info("rtmpPacket init");
        logger.setLevel(Level.TRACE);
        this.byteBuf = byteBuf;
        this.prepare();
    }

    public int getReadIndex() {
        return this.byteBuf.readableBytes();
    }

    public void reset(ByteBuf byteBuf,ChannelHandlerContext ctx) { //新的数据，重新设置
        this.byteBuf = byteBuf;
        copyByteBuf.release();
        prepare();
    }

    private void prepare() {
        if (this.type == 1) {
            logger.info("prepare play");
            logger.info(byteBuf.refCnt());
            logger.info(this.getReadIndex());
        }
        copyByteBuf = byteBuf.copy();
        init();
    }


    /**
     * 数据不足时候数据拷贝
     */
    public void lackMessage(ByteBuf byteBuf) {
        int allMessageLength = byteBuf.readableBytes() + copyByteBuf.readableBytes();
        int chunkLen = 0;
        if(!isLackHead) {
            if(isSubpackage) { //存在 分包情况，以chunkSzie 作为判断依据
                if(this.messageLength - this.readIndex >= chunkLength) {
                    chunkLen = chunkLength + this.head_len + 1;
                } else {
                    chunkLen = this.messageLength - this.readIndex + this.head_len + 1;
                }
            } else {
                chunkLen = this.messageLength + this.head_len + 1;
            }

            if(!chunkByteBufLength(chunkLen, allMessageLength)) { //数据太长，继续拷贝余下部分
//                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
//                logger.info(df.format(new Date()));// new Date()为获取当前系统时间
                if(this.type == 1) {
//                    logger.info("chunkLen == " + chunkLen);
//                    logger.info("allMessageLength == " + allMessageLength);
//                    logger.info("重复拷贝");
                }
                return;
            }
        }
        ByteBuf tmpByteBuf = ByteBufAllocator.DEFAULT.buffer(allMessageLength);
        tmpByteBuf.writeBytes(copyByteBuf);
        tmpByteBuf.writeBytes(byteBuf);
        if(this.byteBuf.refCnt() > 0)
            this.byteBuf.release();
        if(this.type == 1) {
            logger.info("数据不足 释放");
        }
        this.byteBuf = tmpByteBuf.copy();
        tmpByteBuf.release();
        init();
    }

    private void setTimestamp() {
        byte[] timestampByte = new byte[Common.TIMESTAMP_BYTE_LENGTH];
        byteBuf.readBytes(timestampByte);
        timestamp = Common.byteToInt24(timestampByte); // 为了解码 时候 客户端使用 所以 进行类加
    }

    private void setMessageLength() {
        byte[] msg_len = new byte[Common.TIMESTAMP_BYTE_LENGTH];
        byteBuf.readBytes(msg_len);
        this.messageLength = Common.byteToInt24(msg_len);
    }

    private void setMessageType() {
        byte type = byteBuf.readByte();
        switch (type) {
            case 0x01:
                this.messageType = RtmpMessage.ChunkSize;
                break;
            case 0x03:
                this.messageType = RtmpMessage.BYTES_READ;
                break;
            case 0x04:
                this.messageType = RtmpMessage.PING;
                break;
            case 0x05:
                this.messageType = RtmpMessage.SERVER_WINDOW;
                break;
            case 0x08:
                this.messageType = RtmpMessage.AUDIO;
                //  this.audioTimestamp = timestamp;
                break;
            case 0x09:
                this.messageType = RtmpMessage.VIDEO;
                // this.videoTimestamp = timestamp;
                break;
            case 0x12:
                this.messageType = RtmpMessage.NOTIFY;
                break;
            case 0x14:
                this.messageType = RtmpMessage.Control;
                break;
            default:
                this.messageType = RtmpMessage.NOT;
                logger.info("数据大小"+chunkLength);
                logger.info("未知消息" + type);
        }
    }

    private void setStreamId(){
        byte[] streamByte = new byte[Common.STREAM_ID_LENGTH];
        byteBuf.readBytes(streamByte);
        this.streamId = Common.byteSmallToInt(streamByte); //只有 stream 是小端模式
    }

    private void clearByteBuf() {
        byte[] lackData = new byte[this.getReadIndex()];
        this.byteBuf.readBytes(lackData);
    }

    /**
     * 检查是否数据不足
     * @param len
     * @return
     */
    private boolean chunkByteBufLength(int len,int dataLen) {
        if(dataLen < len) {
            lackMessage = true;
//            logger.info("chunkByteBufLength 数据不足");
            return false;
        }
        return true;
    }

    /**
     * 根据头部设置 头部数据
     */
    private boolean setChunkHeaderData() {
        HeadInfo headInfo = this.csidHeadInfo.get(this.csid);
        if(!chunkByteBufLength(this.head_len,this.getReadIndex())) {
            this.clearByteBuf();
            isLackHead = true;
            return false;
        }
        if (this.head_len == 0) {
            if (headInfo != null) {
                this.messageLength = headInfo.getMessageLength();
                this.timestamp = headInfo.getTimestamp();
                this.messageType = headInfo.getMessageType();
            }
        }

        if(this.head_len >= 3) {
            this.setTimestamp();
            if (headInfo != null)  {
                this.messageLength = headInfo.getMessageLength();
                this.messageType = headInfo.getMessageType();
            }

        }


        if(this.head_len >= 7){
            this.setMessageLength();
            this.setMessageType();

        }
        if(this.head_len >= 11) {
            this.setStreamId();
        }

        headInfo = new HeadInfo(this.timestamp, this.messageLength, this.messageType);
        this.csidHeadInfo.put(this.csid, headInfo);



        if((this.head_len == 3 || this.head_len == 7)){ //当头部为3 或者 7，时间戳为跟上一个时间戳的累加
            this.renderTimestamp += timestamp;
        }else if (this.head_len == 11){ //头部为11 t，时间戳直接修改
            //   logger.info("this.head_len" + timestamp);
            this.renderTimestamp = timestamp;
        }

        if(this.timestamp >= Common.TIMESTAMP_MAX_NUM) {
            logger.info("额外数据" + this.timestamp);
            this.head_len = this.head_len + 4;
            if(!chunkByteBufLength(4,this.getReadIndex())) {
                this.clearByteBuf();
                isLackHead = true;
                return false;
            }
            byte[] timestampByte = new byte[Common.EXTEND_TIMESTAMP_LENGTH];
            byteBuf.readBytes(timestampByte);
            this.extTimestamp = Common.byteToInt(timestampByte);
            if(this.messageType == RtmpMessage.AUDIO || this.messageType == RtmpMessage.VIDEO ){
                this.renderTimestamp = this.extTimestamp;
            }
        }
        return true;
    }

    /**
     * 当数据包超过chunkSize 设置时候，需要分包，每个包 head_len为 0
     */
    private void subPackage() {
        isSubpackage = true;
        int chunkLen = 0;
        if(this.messageLength - this.readIndex > chunkLength) {
            chunkLen = chunkLength;
        } else {
            chunkLen = this.messageLength - this.readIndex;
        }
        if(!chunkByteBufLength(chunkLen,getReadIndex())) {
            this.clearByteBuf();
            return;
        }
        byte[] chunkMessageData = new byte[chunkLen];
        byteBuf.readBytes(chunkMessageData);
        for(int i = 0;i < chunkLen;i++) {
            if(readIndex >= this.messageLength){
                SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
                logger.info(df.format(new Date()));// new Date()为获取当前系统时间
                logger.info("超出数据范围");
                break;
            }
            messageData[readIndex++] = chunkMessageData[i];
        }
        if(this.readIndex == this.messageLength) {
            this.readIndex = 0;
            isSubpackage = false;
        }
    }

    /**
     * 设置消息数据
     */
    private void setMessageData() {
        if(!isSubpackage) {
            messageData = new byte[this.messageLength];
        }

        if(this.messageLength > chunkLength) { //大数据,rtmp 分包提取
            subPackage();
            return;
        }

        if(!chunkByteBufLength(this.messageLength,getReadIndex())) {
            if(this.type == 1){
                logger.info("数据不足: messageLength : " + this.messageLength + " , bytebuffer length : " + getReadIndex());
            }
            this.clearByteBuf();
            return;
        }
        messageData = new byte[this.messageLength];
        if(this.getReadIndex() < this.messageLength) {
            logger.info(this.messageLength);
            logger.info(messageData.length);
            return;
        }

        byteBuf.readBytes(messageData);
        prevLen = this.getReadIndex();
        if (this.type == 1){
            logger.info("setMessageData" + this.byteBuf.refCnt());
            logger.info("setMessageData len" + this.getReadIndex());
            logger.info(Common.bytes2hex(messageData));
        }
    }
}


